﻿///    /************************************************************************ 
///     * uoLib library for .Net projects.
///     * Copyright (c) 2009 by uonun
///     * Homepage: http://uolib.ourome.com
///     * 
///     * This program is free software: you can redistribute it and/or modify
///     * it under the terms of the GNU General Public License as published by
///     * the Free Software Foundation, either version 3 of the License, or
///     * (at your option) any later version.

///     * This program is distributed in the hope that it will be useful,
///     * but WITHOUT ANY WARRANTY; without even the implied warranty of
///     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
///     * GNU General Public License for more details.

///     * You should have received a copy of the GNU General Public License
///     * along with this program.  If not, see http://www.gnu.org/licenses/gpl.html.
///     ***********************************************************************/ 

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.IO;
using uoLib.Web.Debugger;

namespace uoLib.Web.HttpHandlers
{
    /// <summary>
    /// 提供异常日志的Web浏览功能
    /// </summary>
    /// <example>
    /// <p>此功能是uoLib提供的特色功能，也是最推荐使用的功能。使用起来非常方便，在项目中引用uoLib之后只需要配置Global.asax即可。</p>
    /// <code><![CDATA[
    /// protected void Application_Error(object sender, EventArgs e)
    /// {
    ///     if (HttpContext.Current.AllErrors.Length > 0)
    ///     { 
    ///         foreach (Exception ex in HttpContext.Current.AllErrors) 
    ///         {
    ///             uoLib.Web.Debugger.ErrorReporter.RecordErrors(ex); 
    ///         }
    ///     }
    /// }]]></code>
    /// <p>这样设置之后，项目中一旦产生了运行时异常将自动被以日志形式记录下来。默认的日志类型是.htm文件，记录在~/Errors/目录下（需要有写入权限），您也可以通过配置<see cref="uoLib.Web.Debugger.Configuration"/>来设置日志类型、存储路径，甚至关闭此功能。</p>
    /// <p>如果您希望能以Web页面的形式访问到系统记录的异常日志，只需要配置web.config中httpHandlers一节即可：</p>
    /// <code><![CDATA[
    /// <?xml version="1.0" encoding="utf-8"?>
    /// <configuration>
    ///     <system.web>
    ///         <httpHandlers>
    ///             <add verb="*" path="Errors/List.aspx" type="uoLib.Web.HttpHandlers.ServerErrorList,uoLib"/>
    ///         </httpHandlers>
    ///     </system.web>
    /// </configuration>]]></code>
    /// <p>其中“Errors/List.aspx”可以是您希望的任意路径，不必存在这个.aspx文件。</p>
    /// <p>这里有一个异常捕获的日志列表的在线示例：<a href="http://www.ourome.com/Errors/list.aspx" target="_blank">http://www.ourome.com/Errors/list.aspx</a>。</p>
    /// </example>
    /// <seealso cref="uoLib.Web.Debugger.ErrorReporter"/>
    /// <seealso cref="uoLib.Web.Debugger.Configuration"/>
    public class ServerErrorList : IHttpHandler
    {
        #region IHttpHandler 成员

        /// <summary>
        /// 是否可以被多线程同时使用
        /// </summary>
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            StringBuilder sb = ShowHeader(context);

            sb.Append(ShowBody(context));

            sb.Append(ShowFooter(context));
            context.Response.Write(sb);
        }
        #endregion

        private static StringBuilder ShowHeader(HttpContext context)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
            sb.AppendLine("<head>");

            sb.AppendLine("<!--=============================================================================");
            sb.AppendLine(string.Format("\tRecorded by uoLib Version {0}, ourome.com", Info.Version));
            sb.AppendLine(string.Format("\tPublic Url: {0}", Info.HelpLink));
            sb.AppendLine();
            sb.AppendLine("\t.Net Framework: " + Environment.Version.ToString());
            sb.AppendLine("\tEnvironment.OSVersion: " + Environment.OSVersion.VersionString);
            sb.AppendLine(string.Format("\tDateTime: {0}", DateTime.Now.ToString()));
            sb.AppendLine("=============================================================================-->");

            sb.AppendLine("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />");

            sb.AppendLine("<title>错误日志列表 [Responsed by uoLib V" + Info.Version.ToString() + ", ourome.com]</title>");

            sb.AppendLine("<style>");
            sb.AppendLine("body{padding:0;margin:0;}");
            sb.AppendLine("body,p,li{font-size:12px;line-height:150%;font-family:Arial;}");
            sb.AppendLine("p{padding-left:2em;}");
            sb.AppendLine(".att {color:#F00;}");
            sb.AppendLine(".detail {color:#999;}");
            sb.AppendLine(".TOP {padding:10px 30px; background-color:#0000AA;}");
            sb.AppendLine("H1 { font-family:\"微软雅黑\",\"宋体\";font-weight:normal;font-size:16pt;color:#FFF;line-height:150%;}");
            sb.AppendLine("a {text-decoration:none;font-family:\"Verdana\";color:#A00}");
            sb.AppendLine("a:visited {color:#777;}");
            sb.AppendLine("a:hover {text-decoration:underline;color:#F00;}");
            sb.AppendLine(".mainContainer ol{margin:0 6em;}");
            sb.AppendLine(".mainContainer ol li{border-bottom:1px dotted #CCC;}");
            sb.AppendLine("</style>");
            sb.AppendLine("</head>");
            sb.AppendLine("<body>");

            sb.Append(string.Format("<div class=\"TOP\"><h1>\"{0}\" 应用程序中的错误日志</h1></div>", context.Request.ApplicationPath));
            sb.AppendLine("");
            return sb;
        }
        private static StringBuilder ShowBody(HttpContext context)
        {
            StringBuilder sb = new StringBuilder();

            #region 删除日志文件
            string act = context.Request.QueryString["act"];
            string file = context.Request.QueryString["file"];
            if (act == "del" && !string.IsNullOrEmpty(file))
            {
                string filePath = context.Server.MapPath(string.Format("{0}{1}", Configuration.LogPath, file));
                FileInfo fi = new FileInfo(filePath);
                try
                {
                    if (fi.Exists) { fi.Delete(); }
                }
                catch (System.UnauthorizedAccessException)
                {
                    sb.Append(string.Format("<p class=\"att\">对路径“{0}”的访问被拒绝。</p>", filePath));
                }
            }
            else if (act == "delAll") {
                DirectoryInfo di = new DirectoryInfo(context.Server.MapPath(Configuration.LogPath));
                try
                {
                    FileInfo[] fis = di.GetFiles();
                    if (fis != null && fis.Length > 0)
                    {
                        foreach (FileInfo f in fis) { f.Delete(); }
                    }
                    context.Response.Redirect(context.Request["SCRIPT_NAME"]);
                }
                catch(Exception ex) {
                    sb.Append(string.Format("<p class=\"att\">发生错误：{0}</p>", ex.Message));
                }
            }
            #endregion

            #region 显示日志文件列表
            sb.Append("<div class=\"mainContainer\">");
            if (Configuration.IsRecordErrors == false)
            {
                sb.Append("<p class=\"att\">已停止记录错误日志。</p>");
            }
            else
            {
                DirectoryInfo di = new DirectoryInfo(context.Server.MapPath(Configuration.LogPath));
                if (!di.Exists)
                {
                    sb.Append(string.Format("<p class=\"att\">日志存储目录不存在。（{0}）</p>", di.FullName));
                }
                else
                {
                    try
                    {
                        FileInfo[] files = di.GetFiles("*.*", SearchOption.TopDirectoryOnly);
                        if (files == null || files.Length == 0)
                        {
                            sb.Append("<p>尚无错误日志。</p>");
                        }
                        else
                        {
                            sb.Append(string.Format("<p>以下错误日志由 uoLib 自动记录，您可以在Global.asax中配置日志的存储路径、文件类型或关闭日志记录。[<a href=\"{0}\" target=\"_blank\">查看帮助</a> | <a href=\"?act=delAll\">删除所有记录</a>]</p>", uoLib.Info.HelpLink));

                            sb.Append("<ol>");
                            foreach (FileInfo fi in files)
                            {
                                sb.Append("<li>");
                                string detail = "";
                                string fileSize = "";
                                using (StreamReader sr = new StreamReader(fi.FullName, Encoding.UTF8))
                                {
                                    string tmp = sr.ReadToEnd();
                                    sr.Close();
                                    //<ErrorMessage>标签来自于ShowServerError。
                                    Regex re = new Regex("<ErrorMessage>(.*)</ErrorMessage>");
                                    detail = re.Match(tmp).Value;
                                    fileSize = "(" + uoLib.Functions.FormatFileSize(fi.Length) + ")";
                                }
                                sb.Append(string.Format("<a href=\"{1}{0}\" target=\"_blank\">{0}</a> - <a href=\"?act=del&file={0}\">[删除]</a> {3} <span class=\"detail\">{2}</span>", fi.Name, Configuration.LogPath.TrimStart('~'), detail, fileSize));
                                sb.Append("</li>");
                            }
                            sb.Append("</ol>");
                        }
                    }
                    catch (System.UnauthorizedAccessException)
                    {
                        sb.Append(string.Format("<p class=\"att\">对路径“{0}”的访问被拒绝。</p>", di.FullName));
                    }
                }
            }
            sb.Append("</div>");
            #endregion

            return sb;
        }
        private static StringBuilder ShowFooter(HttpContext context)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<br /><br />");
            sb.Append(string.Format("<p><a href=\"{0}\" target=\"_blank\">Responsed by uoLib V{1}, ourome.com</a></p>", uoLib.Info.HelpLink, uoLib.Info.Version));
            sb.AppendLine("</body></html>");

            return sb;
        }
    }
}
